﻿#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <thread>
using namespace std;

double Divide(double a, double b)
{
	try 
	{
		if (0 == b)
		{
			string s = "divided by zero condition!";
			//const char* s = "divided by zero condition!";

			throw s;

			// throw执行时，其后的语句不再执行
			cout << "Divide:抛出异常后这里不会执行" << endl;
		}
		else
		{
			return a / b;
		}
	}
	catch (int errid)
	{
		cout << errid << endl;
	}

	return 0;
}

void Func()
{
	double a = 0, b = 0;
	cin >> a >> b;

	try
	{
		cout << Divide(a, b) << endl << endl;
	}
	catch (const char* errmsg)	// 匹配throw抛出的const char*类型的对象
	{
		cout << __FUNCTION__ << ": ";
		cout << errmsg << endl;
	}

	cout << __FUNCTION__ << " : ";
	cout << "catch 后续的代码继续执行" << endl;
}

void Test1()
{
	for (int i = 3; i; --i)
	{
		try
		{
			Func();
		}
		catch (const string& s)	// 匹配throw抛出的string类型的对象
		{
			cout << __FUNCTION__ << ": ";
			cout << s << endl;
		}

		cout << __FUNCTION__ << ": ";
		cout << "catch 后续的代码继续执行" << endl << endl;
	}
}

class Exception
{
public:
	Exception(int id, const string& errmsg)
		: _id(id)
		, _errmsg(errmsg)
	{}

	virtual string what() const
	{
		return _errmsg;
	}

	const int& getid() const
	{
		return _id;
	}

protected:
	int _id;
	string _errmsg;
};

class SqlException : public Exception
{
public:
	SqlException(int id, const string& errmsg, const string& sql)
		: Exception(id, errmsg)
		, _sql(sql)
	{}

	virtual string what() const
	{
		string str = "SqlException ";
		str += _errmsg;
		str += "->";
		str += _sql;

		return str;
	}

private:
	const string _sql;
};

class CacheException : public Exception
{
public:
	CacheException(int id, const string& errmsg)
		: Exception(id, errmsg)
	{}

	virtual string what() const
	{
		string str = "CacheException ";
		str += _errmsg;

		return str;
	}
};

class HttpException : public Exception
{
public:
	HttpException(int id, const string& errmsg, const string& type)
		: Exception(id, errmsg)
		, _type(type)
	{}

	virtual string what() const
	{
		string str = "HttpException ";
		str += _type;
		str += ":";
		str += _errmsg;

		return str;
	}

private:
	const string _type;
};

void SQLMgr()
{
	if (0 == rand() % 7)
	{
		throw SqlException(100, "权限不足", "select * from name = '张三'");
	}
	else
	{
		cout << "SQLMgr 调用成功" << endl;
	}
}

void CacheMgr()
{
	if (0 == rand() % 5)
	{
		throw CacheException(100, "权限不足");
	}
	else if (0 == rand() % 6)
	{
		throw CacheException(101, "数据不存在");
	}
	else
	{
		cout << "CacheMgr 调用成功" << endl;
	}

	SQLMgr();
}

void HttpServer()
{
	if (0 == rand() % 3)
	{
		throw HttpException(100, "请求资源不存在", "get");
	}
	else if (0 == rand() % 4)
	{
		throw HttpException(101, "权限不足", "post");
	}
	else
	{
		cout << "HttpServer 调用成功" << endl;
	}

	CacheMgr();
}

void Test2()
{
	srand(static_cast<unsigned>(time(0)));

	for (int i = 12; i; --i)
	{
		this_thread::sleep_for(chrono::seconds(1));// 通过chrono库指定当前线程休眠的单位是秒seconds，Windows/Linux通用

		try
		{
			HttpServer();
		}
		catch (const Exception& e)
		{
			cout << "【" << e.what() << "】" << endl;
		}
		catch (...)
		{
			cout << "Unknown Exception" << endl;
		}
	}
}

// 模拟聊天时发送消息，消息发送失败时需要捕获异常
// 在电梯地下室等⼿机信号不好的地方，消息发送失败时可以进行尝试重发，如果所有尝试全部失败，网络条件不满足，停止消息的复发
// 此时可以将捕获的异常重新抛出。如果不是⽹络差导致的错误，捕获后重新抛出，交由其它位置去处理
void _SendMsg(const string& s)
{
	if (0 == rand() % 2)
	{
		throw HttpException(102, "网络不稳定", "put");
	}
	else if (0 == rand() % 7)
	{
		throw HttpException(103, "你不是对方的好友，发送失败", "put");
	}
	else
	{
		cout << "发送成功" << endl << endl;
	}
}

// 发送消息失败后，再重试3次
void SendMsg(const string& s)
{
	for (size_t i = 0; i < 4; ++i)
	{
		try
		{
			_SendMsg(s);
			break;
		}
		catch (const Exception& e)
		{
			// 捕获异常。102号错误，说明⽹络不稳定，重试3次
			// 捕获异常。不是102号错误，将异常重新抛出
			if (102 == e.getid())
			{
				// 重试3次全部失败，说明⽹络太差，重新抛出异常
				if (3 == i)
				{
					cout << s << " 发送失败" << endl;
					throw;
				}

				cout << "开始第 " << i + 1 << " 次重发..." << endl;
			}
			else
			{
				throw;
			}
		}
	}
}

void Test3()
{
	const int N = 5;
	srand(static_cast<unsigned>(time(0)));

	for (int i = 1; i <= N; ++i)
	{
		try
		{
			cout << i << endl;
			SendMsg(to_string(i));
		}
		catch (const Exception& e)
		{
			cout << "【" << e.what() << "】" << endl << endl;
		}
		catch (...)
		{
			cout << "Unknown Exception" << endl;
		}
	}
}

namespace yyp
{
	class oop :public exception
	{
		const char* what() const
		{
			return "【Division by zero condition!】";
		}
	};

	double Divide(double a, double b)
	{
		if (0 == b)
		{
			throw "Division by zero condition!";
			//throw oop();
		}
		
		return a / b;
	}
}

void Func1()
{
	int* arr = new int[10];

	try
	{
		double a = 0, b = 0;
		cin >> a >> b;

		cout << yyp::Divide(a, b) << endl;
	}
	catch (...)
	{
		cout << "delete[] " << arr << endl;
		delete[] arr;

		throw;
	}

	cout << "delete[] " << arr << endl;
	delete[] arr;
}

void Test4()
{
	/*try
	{
		Func1();
	}
	catch (const char* errmsg)
	{
		cout << "【" << "const char* " << errmsg << "】" << endl << endl;
	}
	catch (const exception& e)
	{
		cout << "【" << "const exception& " << e.what() << "】" << endl << endl;
	}
	catch (...)
	{
		cout << "【" << "Unknown Exception" << "】" << endl << endl;
	}*/

	try
	{
		Func1();
	}
	catch (const char* errmsg)
	{
		cout << "【" << errmsg << "】" << endl << endl;
	}
	catch (...)
	{
		cout << "【" << "Unknown Exception" << "】" << endl << endl;
	}
}

// vs2019 debug x86
// warning “Divide” : 假定函数不引发异常，但确实发生了
double Divide1(double a, double b) noexcept
{
	if (b == 0)
	{
		throw "Division by zero condition!";
	}
	return a / b;
}

void Test5()
{
	try
	{
		double a, b;
		cin >> a >> b;
		cout << Divide1(a, b) << endl;
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	catch (...)
	{
		cout << "Unkown Exception" << endl;
	}
	cout << endl;

	int i = 0;
	cout << noexcept(Divide1(1, 2)) << endl;
	cout << noexcept(Divide1(1, 0)) << endl;
	cout << noexcept(++i) << endl << endl;

	string s = "123";
	cout << noexcept(s.begin()) << endl;
	cout << noexcept(string("123").begin()) << endl;
	cout << noexcept(string().begin()) << endl << endl;

	vector<int> v;
	cout << noexcept(v.begin()) << endl;
	cout << noexcept(vector<int>({1, 2, 3}).begin()) << endl;
	cout << noexcept(vector<int>().begin()) << endl;
}

#include <iostream>
#include <exception>
using namespace std;

// 从重写完成的那一刻开始，携带的异常信息就无法更改
struct Myep : exception 
{
	const char* what() const noexcept 
	{ return "Myep：exception!!!"; }
};

void Test6() 
{
	try 
	{
		throw Myep();
	}
	catch (const exception& ex) 
	{
		cout << ex.what() << endl;
	}
}

int main()
{
	//Test1();
	//Test2();
	//Test3();
	//Test4();
	//Test5();
	Test6();

	return 0;
}